#/**@file
# Low leve IA32 specific debug support functions.
#
# Copyright (c) 2006 - 2008, Intel Corporation. <BR>
# All rights reserved. This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#**/

ASM_GLOBAL ASM_PFX(OrigVector)
ASM_GLOBAL ASM_PFX(InterruptEntryStub)
ASM_GLOBAL ASM_PFX(StubSize)
ASM_GLOBAL ASM_PFX(CommonIdtEntry)
ASM_GLOBAL ASM_PFX(FxStorSupport)

ASM_PFX(StubSize):       .long   ASM_PFX(InterruptEntryStubEnd) - ASM_PFX(InterruptEntryStub)
ASM_PFX(AppEsp):         .long   0x11111111 # ?
ASM_PFX(DebugEsp):       .long   0x22222222 # ?
ASM_PFX(ExtraPush):      .long   0x33333333 # ?
ASM_PFX(ExceptData):     .long   0x44444444 # ?
ASM_PFX(Eflags):         .long   0x55555555 # ?
ASM_PFX(OrigVector):     .long   0x66666666 # ?

#------------------------------------------------------------------------------
# BOOLEAN
# FxStorSupport (
#   void
#   )
#
# Abstract: Returns TRUE if FxStor instructions are supported
#
ASM_GLOBAL ASM_PFX(FxStorSupport)
ASM_PFX(FxStorSupport):
#
# cpuid corrupts ebx which must be preserved per the C calling convention
#
        push   %ebx
        mov    $0x1,%eax
        cpuid
        mov    %edx,%eax
        and    $0x1000000,%eax
        shr    $0x18,%eax
        pop    %ebx
        ret
#------------------------------------------------------------------------------
# void
# Vect2Desc (
#   DESCRIPTOR * DestDesc,
#   void (*Vector) (void)
#   )
#
# Abstract: Encodes an IDT descriptor with the given physical address
#

ASM_GLOBAL ASM_PFX(Vect2Desc)
ASM_PFX(Vect2Desc):
        push   %ebp
        mov    %esp,%ebp
        mov    0xc(%ebp),%eax
        mov    0x8(%ebp),%ecx
        mov    %ax,(%ecx)
        movw   $0x20,0x2(%ecx)
        movw   $0x8e00,0x4(%ecx)
        shr    $0x10,%eax
        mov    %ax,0x6(%ecx)
        leave
        ret

ASM_GLOBAL ASM_PFX(InterruptEntryStub)
ASM_PFX(InterruptEntryStub):
        mov    %esp,0x0                    # save stack top
        mov    $0x0,%esp                   # switch to debugger stack
        push   $0x0                        # push vector number - will be modified before installed
        jmp    ASM_PFX(CommonIdtEntry)     # jump CommonIdtEntry
ASM_GLOBAL ASM_PFX(InterruptEntryStubEnd)
ASM_PFX(InterruptEntryStubEnd):

#------------------------------------------------------------------------------
# CommonIdtEntry
#
# Abstract: This code is not a function, but is the common part for all IDT
#               vectors.
#
ASM_GLOBAL ASM_PFX(CommonIdtEntry)
ASM_PFX(CommonIdtEntry):
##
## At this point, the stub has saved the current application stack esp into AppEsp
## and switched stacks to the debug stack, where it pushed the vector number
##
## The application stack looks like this:
##
##              ...
##              (last application stack entry)
##              eflags from interrupted task
##              CS from interrupted task
##              EIP from interrupted task
##              Error code <-------------------- Only present for some exeption types
##
##


## The stub switched us to the debug stack and pushed the interrupt number.
##
## Next, construct the context record.  It will be build on the debug stack by
## pushing the registers in the correct order so as to create the context structure
## on the debug stack.  The context record must be built from the end back to the
## beginning because the stack grows down...
#
## For reference, the context record looks like this:
##
## typedef
## struct {
##   UINT32             ExceptionData;
##   FX_SAVE_STATE_IA32 FxSaveState;
##   UINT32             Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
##   UINT32             Cr0, Cr2, Cr3, Cr4;
##   UINT32             EFlags;
##   UINT32             Ldtr, Tr;
##   UINT32             Gdtr[2], Idtr[2];
##   UINT32             Eip;
##   UINT32             Gs, Fs, Es, Ds, Cs, Ss;
##   UINT32             Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
## } SYSTEM_CONTEXT_IA32;  // 32 bit system context record

## UINT32  Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
        pusha
## Save interrupt state eflags register...
        pushf
        pop    %eax
## We need to determine if any extra data was pushed by the exception, and if so, save it
## To do this, we check the exception number pushed by the stub, and cache the
## result in a variable since we'll need this again.
        mov    %eax,0x0
        cmpl   $0x8,0x0
        jne    ASM_PFX(CommonIdtEntry+0x20)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0xa,0x0
        jne    ASM_PFX(CommonIdtEntry+0x35)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0xb,0x0
        jne    ASM_PFX(CommonIdtEntry+0x4a)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0xc,0x0
        jne    ASM_PFX(CommonIdtEntry+0x5f)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0xd,0x0
        jne    ASM_PFX(CommonIdtEntry+0x74)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0xe,0x0
        jne    ASM_PFX(CommonIdtEntry+0x89)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        cmpl   $0x11,0x0
        jne    ASM_PFX(CommonIdtEntry+0x9e)
        movl   $0x1,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xa8)
        movl   $0x0,0x0
## If there's some extra data, save it also, and modify the saved AppEsp to effectively
## pop this value off the application's stack.

        cmpl   $0x1,0x0
        jne    ASM_PFX(CommonIdtEntry+0xc8)
        mov    0x0,%eax
        mov    (%eax),%ebx
        mov    %ebx,0x0
        add    $0x4,%eax
        mov    %eax,0x0
        jmp    ASM_PFX(CommonIdtEntry+0xd2)
        movl   $0x0,0x0
## The "pushad" above pushed the debug stack esp.  Since what we're actually doing
## is building the context record on the debug stack, we need to save the pushed
## debug ESP, and replace it with the application's last stack entry...
        mov    0xc(%esp),%eax
        mov    %eax,0x0
        mov    0x0,%eax
        add    $0xc,%eax
        # application stack has eflags, cs, & eip, so
        # last actual application stack entry is
        # 12 bytes into the application stack.
        mov    %eax,0xc(%esp)
## continue building context record
## UINT32  Gs, Fs, Es, Ds, Cs, Ss;  insure high 16 bits of each is zero
        mov    %ss,%eax
        push   %eax
        
        # CS from application is one entry back in application stack
        mov    0x0,%eax
        movzwl 0x4(%eax),%eax
        push   %eax
        mov    %ds,%eax
        push   %eax
        mov    %es,%eax
        push   %eax
        mov    %fs,%eax
        push   %eax
        mov    %gs,%eax
        push   %eax

## UINT32  Eip;
        # Eip from application is on top of application stack
        mov    0x0,%eax
        pushl  (%eax)

## UINT32  Gdtr[2], Idtr[2];
        push   $0x0
        push   $0x0
        sidtl  (%esp)
        push   $0x0
        push   $0x0
        sgdtl  (%esp)

## UINT32  Ldtr, Tr;
        xor    %eax,%eax
        str    %eax
        push   %eax
        sldt   %eax
        push   %eax

## UINT32  EFlags;
## Eflags from application is two entries back in application stack
        mov    0x0,%eax
        pushl  0x8(%eax)

## UINT32  Cr0, Cr1, Cr2, Cr3, Cr4;
## insure FXSAVE/FXRSTOR is enabled in CR4...
## ... while we're at it, make sure DE is also enabled...
        mov    %cr4,%eax
        or     $0x208,%eax
        mov    %eax,%cr4
        push   %eax
        mov    %cr3,%eax
        push   %eax
        mov    %cr2,%eax
        push   %eax
        push   $0x0
        mov    %cr0,%eax
        push   %eax

## UINT32  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
        mov    %db7,%eax
        push   %eax

## clear Dr7 while executing debugger itself
        xor    %eax,%eax
        mov    %eax,%db7
        mov    %db6,%eax
        push   %eax

## insure all status bits in dr6 are clear...
        xor    %eax,%eax
        mov    %eax,%db6
        mov    %db3,%eax
        push   %eax
        mov    %db2,%eax
        push   %eax
        mov    %db1,%eax
        push   %eax
        mov    %db0,%eax
        push   %eax

## FX_SAVE_STATE_IA32 FxSaveState;
        sub    $0x200,%esp
        mov    %esp,%edi
        # IMPORTANT!! The debug stack has been carefully constructed to
        # insure that esp and edi are 16 byte aligned when we get here.
        # They MUST be.  If they are not, a GP fault will occur.
        fxsave (%edi)

## UINT32  ExceptionData;
        mov    0x0,%eax
        push   %eax

# call to C code which will in turn call registered handler
# pass in the vector number
        mov    %esp,%eax
        push   %eax
        mov    0x0,%eax
        push   %eax
        call   ASM_PFX(CommonIdtEntry+0x184)
        add    $0x8,%esp

# restore context...
## UINT32  ExceptionData;
        add    $0x4,%esp

## FX_SAVE_STATE_IA32 FxSaveState;
        mov    %esp,%esi
        fxrstor (%esi)
        add    $0x200,%esp

## UINT32  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
        pop    %eax
        mov    %eax,%db0
        pop    %eax
        mov    %eax,%db1
        pop    %eax
        mov    %eax,%db2
        pop    %eax
        mov    %eax,%db3

## skip restore of dr6.  We cleared dr6 during the context save.
        add    $0x4,%esp
        pop    %eax
        mov    %eax,%db7

## UINT32  Cr0, Cr1, Cr2, Cr3, Cr4;
        pop    %eax
        mov    %eax,%cr0
        add    $0x4,%esp
        pop    %eax
        mov    %eax,%cr2
        pop    %eax
        mov    %eax,%cr3
        pop    %eax
        mov    %eax,%cr4

## UINT32  EFlags;
        mov    0x0,%eax
        popl   0x8(%eax)

## UINT32  Ldtr, Tr;
## UINT32  Gdtr[2], Idtr[2];
## Best not let anyone mess with these particular registers...
        add    $0x18,%esp

## UINT32  Eip;
        popl   (%eax)

## UINT32  SegGs, SegFs, SegEs, SegDs, SegCs, SegSs;
## NOTE - modified segment registers could hang the debugger...  We
##        could attempt to insulate ourselves against this possibility,
##        but that poses risks as well.
##

        pop    %gs
        pop    %fs
        pop    %es
        pop    %ds
        popl   0x4(%eax)
        pop    %ss
        mov    0xc(%esp),%ebx

## The next stuff to restore is the general purpose registers that were pushed
## using the "pushad" instruction.
##
## The value of ESP as stored in the context record is the application ESP
## including the 3 entries on the application stack caused by the exception
## itself. It may have been modified by the debug agent, so we need to
## determine if we need to relocate the application stack.

        mov    0x0,%eax          # move the potentially modified AppEsp into ebx
        add    $0xc,%eax
        cmp    %eax,%ebx
        je     ASM_PFX(CommonIdtEntry+0x202)
        mov    0x0,%eax
        mov    (%eax),%ecx       # EIP
        mov    %ecx,(%ebx)
        mov    0x4(%eax),%ecx    # CS
        mov    %ecx,0x4(%ebx)
        mov    0x8(%eax),%ecx    # EFLAGS
        mov    %ecx,0x8(%ebx)
	
        mov    %ebx,%eax         # modify the saved AppEsp to the new AppEsp
        mov    %eax,0x0
        mov    0x0,%eax          # restore the DebugEsp on the debug stack
	                               # so our "popad" will not cause a stack switch
        mov    %eax,0xc(%esp)    
        cmpl   $0x68,0x0
        jne    PhonyIretd+0xd
## Restore eflags so when we chain, the flags will be exactly as if we were never here.
## We gin up the stack to do an iretd so we can get ALL the flags.
        mov    0x0,%eax
        mov    0x8(%eax),%ebx
        and    $0xfffffcff,%ebx  # special handling for IF and TF
        push   %ebx
        push   %cs
        push   $0x0
        iret

PhonyIretd:
## UINT32  Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
        popa

## Switch back to application stack
        mov    0x0,%esp
        jmp    *0x0
## Jump to original handler
## UINT32  Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
        popa
## Switch back to application stack
        mov    0x0,%esp

## We're outa here...
        iret
